
#include "../../../xcom/xcom.hpp"
#include "../../../xcom/xptrace.hpp"
#include "syscall_name.h"
#include "assert.h"
#include <sys/syscall.h>

void wait_syscall(pid_t child)
{
    // 1.唤醒
    int child_status = 0;
    auto ret = ptrace(PTRACE_SYSCALL, child, 0, 0);
    if (ret < 0)
    {
        X_P_INFO;
    }

    // 2. 等待
    wait(&child_status);

    // 3. 是否已退出？
    if (WIFEXITED(child_status))
    {
        printf("exited in syscalls with status=%d\n", child_status);
        exit(0);
    }
}

static const char *print_reg(uint64_t reg)
{
    static char sz[100] = {0};
    memset(sz, 0, sizeof(sz));
    int64_t int_val = (int64_t)reg;
    if (int_val < 0 && int_val > -1000)
    {
        sprintf(sz, "%lld", int_val);
        return sz;
    }

    if (reg > 0x1000000)
    {
        sprintf(sz, "%p", reg);
        return sz;
    }

    sprintf(sz, "%llu", reg);
    return sz;
}

struct syscall_info
{
    uint64_t syscall_no = 0;
    uint64_t syscall_ret = 0;
    uint64_t para[6] = {0};

    void print()
    {
        const char *syscall_name = syscall_table[syscall_no];
        printf("%s", syscall_name);
        printf("(");

        for (int i = 0; i < get_syscall_para_cnt(syscall_name); ++i)
        {
            if (0 != i)
            {
                printf(", ");
            }
            printf("%s", print_reg(para[i]));
        }

        printf(")");
        printf(" = %s\n", print_reg(syscall_ret));
    }
    void set_after_call(const user_regs_struct &reg)
    {
        syscall_ret = reg.rax;
        para[0] = reg.rdi;
        para[1] = reg.rsi;
        para[2] = reg.rdx;
        para[3] = reg.r10;
        para[4] = reg.r8;
        para[5] = reg.r9;
    }

    void set_before_call(const user_regs_struct &reg)
    {
        syscall_no = reg.orig_rax;
    }
};

void tracer(pid_t child)
{
    // 等待tracee变为stop状态
    int child_status = 0;
    wait(&child_status);
    printf("child_status=%s\n", xx_waitstate2str(child_status).c_str());

    while (1)
    {
        // 调用前拦截
        syscall_info info;
        wait_syscall(child);
        {
            // 获取寄存器信息
            struct user_regs_struct reg;
            bool get_reg = xx_trace_get_reg(child, reg);
            assert(get_reg);
            // 获取：system call 序号
            info.set_before_call(reg);
        }
        // 调用后拦截
        wait_syscall(child);
        {
            // 获取寄存器信息
            struct user_regs_struct reg;
            bool get_reg = xx_trace_get_reg(child, reg);
            assert(get_reg);
            // 获取：参数、返回值
            info.set_after_call(reg);
        }
        info.print();
    }
}

void tracee(int argc, char *argv[])
{
    xx_trace_me();
    execv(argv[0], argv);
}

int main(int argc, char *argv[])
{

    const char *spid = xx_get_arg(argc, argv, "-p");
    // atach模式
    if (nullptr != spid)
    {
        pid_t pid = atoi(spid);
        xx_trace_attach(pid); // attch
        tracer(pid);          // 开始跟踪system call
    }
    // strace启动模式
    else
    {
        pid_t pid = fork();
        if (0 == pid)
        {
            tracee(argc - 1, argv + 1); // 执行tracee指令
        }
        else if (pid > 0)
        {
            tracer(pid); // 开始跟踪system call
        }
    }

    return 0;
}